﻿using ZeroSum.IdGenerator.Configurations;
using ZeroSum.IdGenerator.Contract;

namespace ZeroSum.IdGenerator.Core;

/// <summary>
///     雪花漂移算法
/// </summary>
internal class SnowWorkerM1 : ISnowWorker
{
    protected static readonly object SyncLock = new();

    /// <summary>
    ///     基础时间
    /// </summary>
    protected readonly DateTime BaseTime;

    /// <summary>
    ///     最大序列数（含）
    /// </summary>
    protected readonly int MaxSeqNumber;

    /// <summary>
    ///     最小序列数（含）
    /// </summary>
    protected readonly ushort MinSeqNumber;

    /// <summary>
    ///     自增序列数位长
    /// </summary>
    protected readonly byte SeqBitLength;

    /// <summary>
    ///     机器码
    /// </summary>
    protected readonly ushort WorkerId;

    /// <summary>
    ///     机器码位长
    /// </summary>
    protected readonly byte WorkerIdBitLength;

    protected ushort CurrentSeqNumber;
    protected int GenCountInOneTerm;

    protected bool IsOverCost;
    protected long LastTimeTick; // -1L
    protected int OverCostCountInOneTerm;
    protected int TermIndex;

    protected byte TimestampShift;

    /// <summary>
    ///     最大漂移次数（含）
    /// </summary>
    protected int TopOverCostCount;

    protected byte TurnBackIndex;
    protected long TurnBackTimeTick; // -1L;

    //private static long _StartTimeTick = 0;
    //private static long _BaseTimeTick = 0;

    public SnowWorkerM1(IdGeneratorOptions options)
    {
        // 1.BaseTime
        if (options.BaseTime != DateTime.MinValue) BaseTime = options.BaseTime;

        // 2.WorkerIdBitLength
        if (options.WorkerIdBitLength == 0)
            WorkerIdBitLength = 6;
        else
            WorkerIdBitLength = options.WorkerIdBitLength;

        // 3.WorkerId
        WorkerId = options.WorkerId;

        // 4.SeqBitLength
        if (options.SeqBitLength == 0)
            SeqBitLength = 6;
        else
            SeqBitLength = options.SeqBitLength;

        // 5.MaxSeqNumber
        if (MaxSeqNumber == 0)
            MaxSeqNumber = (1 << SeqBitLength) - 1;
        else
            MaxSeqNumber = options.MaxSeqNumber;

        // 6.MinSeqNumber
        MinSeqNumber = options.MinSeqNumber;

        // 7.Others
        TopOverCostCount = options.TopOverCostCount;
        if (TopOverCostCount == 0) TopOverCostCount = 2000;

        TimestampShift = (byte) (WorkerIdBitLength + SeqBitLength);
        CurrentSeqNumber = options.MinSeqNumber;
    }


    public virtual long NextId()
    {
        lock (SyncLock)
        {
            return IsOverCost ? NextOverCostId() : NextNormalId();
        }
    }


    protected virtual long NextOverCostId()
    {
        var currentTimeTick = GetCurrentTimeTick();

        if (currentTimeTick > LastTimeTick)
        {
            if (TermIndex > 10000) TermIndex = 0;

            LastTimeTick = currentTimeTick;
            CurrentSeqNumber = MinSeqNumber;
            IsOverCost = false;
            OverCostCountInOneTerm = 0;
            GenCountInOneTerm = 0;

            return CalcId(LastTimeTick);
        }

        if (OverCostCountInOneTerm >= TopOverCostCount)
        {
            if (TermIndex > 10000) TermIndex = 0;

            // TODO: 在漂移终止，等待时间对齐时，如果发生时间回拨较长，则此处可能等待较长时间。可优化为：在漂移终止时增加时间回拨应对逻辑。（该情况发生概率很低）

            LastTimeTick = GetNextTimeTick();
            CurrentSeqNumber = MinSeqNumber;
            IsOverCost = false;
            OverCostCountInOneTerm = 0;
            GenCountInOneTerm = 0;

            return CalcId(LastTimeTick);
        }

        if (CurrentSeqNumber > MaxSeqNumber)
        {
            LastTimeTick++;
            CurrentSeqNumber = MinSeqNumber;
            IsOverCost = true;
            OverCostCountInOneTerm++;
            GenCountInOneTerm++;

            return CalcId(LastTimeTick);
        }

        GenCountInOneTerm++;
        return CalcId(LastTimeTick);
    }

    protected virtual long NextNormalId()
    {
        var currentTimeTick = GetCurrentTimeTick();

        if (currentTimeTick < LastTimeTick)
        {
            if (TurnBackTimeTick >= 1)
                return CalcTurnBackId(TurnBackTimeTick);
            TurnBackTimeTick = LastTimeTick - 1;
            TurnBackIndex++;

            // 每毫秒序列数的前5位是预留位，0用于手工新值，1-4是时间回拨次序
            // 支持4次回拨次序（避免回拨重叠导致ID重复），可无限次回拨（次序循环使用）。
            if (TurnBackIndex > 4) TurnBackIndex = 1;
            //Thread.Sleep(1);
            return CalcTurnBackId(TurnBackTimeTick);
        }

        // 时间追平时，_TurnBackTimeTick清零
        if (TurnBackTimeTick > 0) TurnBackTimeTick = 0;

        if (currentTimeTick > LastTimeTick)
        {
            LastTimeTick = currentTimeTick;
            CurrentSeqNumber = MinSeqNumber;

            return CalcId(LastTimeTick);
        }

        if (CurrentSeqNumber <= MaxSeqNumber)
            return CalcId(LastTimeTick);

        TermIndex++;
        LastTimeTick++;
        CurrentSeqNumber = MinSeqNumber;
        IsOverCost = true;
        OverCostCountInOneTerm = 1;
        GenCountInOneTerm = 1;

        return CalcId(LastTimeTick);
    }

    protected virtual long CalcId(in long useTimeTick)
    {
        var result = (useTimeTick << TimestampShift) +
                     ((long) WorkerId << SeqBitLength) +
                     CurrentSeqNumber;

        CurrentSeqNumber++;
        return result;
    }

    protected virtual long CalcTurnBackId(in long useTimeTick)
    {
        var result = (useTimeTick << TimestampShift) +
                     ((long) WorkerId << SeqBitLength) + TurnBackIndex;

        TurnBackTimeTick--;
        return result;
    }

    protected virtual long GetCurrentTimeTick()
    {
        return (long) (DateTime.UtcNow - BaseTime).TotalMilliseconds;
    }

    protected virtual long GetNextTimeTick()
    {
        var tempTimeTicker = GetCurrentTimeTick();

        while (tempTimeTicker <= LastTimeTick) tempTimeTicker = GetCurrentTimeTick();

        return tempTimeTicker;
    }
}